/* 
 * File:   Detector.cpp
 * Author: liuyi
 * 
 * Created on July 21, 2009, 3:13 PM
 */

#include "Detector.h"
#include "Log.h"

#include <cstring>
#include <cmath>

#ifdef _DEBUG
#include "Debug.h"
#endif

void Detector::detect(vector<ROI *> &roiList, const uchar *greyData, int width, int height) {
    ROI *roi = NULL;
    int upper, lower;

    uchar *buffer = new uchar[width];
    for (int i = s_INTERVAL; i < height; i += s_INTERVAL) {
        if (isPotential(greyData + width * i, width, buffer)) {
            // extention will take place so that use 'upper > 3' here
            for(upper = i-1; upper > 3; upper--) {
                if(!isPotential(greyData + width*upper, width, buffer) && !isPotential(greyData + width*(upper - 1), width, buffer)) {
                    break;
                }
            }

            // extention will take place so that use 'end < height-4' here
            for(lower = i+1; lower < height-4; lower++) {
                if(!isPotential(greyData + width*lower, width, buffer) && !isPotential(greyData + width*(lower + 1), width, buffer)) {
                    break;
                }
            }
            
            i = lower + 1;
            
            if(lower - upper < s_INTERVAL - 1) {  // ignore height
                continue;
            }

            // extention takes place here
            upper -= 4;
            lower += 4;

            vector<int> start2end;
            fetchSubtitle(greyData + width * upper, width, lower - upper + 1, start2end);

            if (start2end.size() > 0) {
                int start, end;
                int size = start2end.size() / 2;
                for(int j = 0; j < size; j++) {
                    start = start2end.at(j*2);
                    end = start2end.at(j*2 + 1);
                    
                    roi = new ROI;
                    roi->width = end - start;
                    roi->height = lower - upper + 1;
                    roi->data = new uchar[roi->width * roi->height];
                    for(int j = 0; j < roi->height; j++) {
                        memcpy(roi->data + roi->width*j, greyData + width*(upper + j) + start, roi->width * sizeof (uchar));
                    }
                    roiList.push_back(roi);

#ifdef _DEBUG
                    //Debug::showGreyImageData(roi->data, roi->width, roi->height);
                    //Debug::normalizeAndShowHist(roi->data + roi->width * (roi->height / 2), roi->width);

                    /*
                    int data[256];
                    memset(data, 0, 256 * sizeof (int));

                    for (int i = 0; i < roi->width * roi->height; i++) {
                        if(roi->data[i] < 125) {
                            roi->data[i] = 0;
                        }
                    }
                    //Debug::showGreyImageData(roi->data, roi->width, roi->height);
                    for (int i = 0; i < roi->width * roi->height; i++) {
                        if(roi->data[i] > 0) {
                            data[roi->data[i]]++;
                        }
                    }
                    Debug::normalizeAndShowHist(data, 256);
                    */
#endif
                }
            }
        }
    }

    delete []buffer;
}

bool Detector::isPotential(const uchar *greyData, int len, uchar *buffer) {
    buffer[0] = buffer[len-1] = NOTEDGE;
    for(int i = 1; i < len - 1; i++) {
        buffer[i] = (abs(greyData[i+1]*1.0f - greyData[i-1]) > s_EDGE_THETA)? ISEDGE: NOTEDGE;
    }

    bool potential = false;
    const uchar *bp = NULL; // buffer pointer
    const uchar *bep = NULL; // end pointer in iteration
    int jump;
    
    for(int i = 0; i + s_WIN_SIZE < len; i += s_WIN_SIZE / 2) {
        jump = 0;
        for (bp = buffer + i, bep = buffer + i + s_WIN_SIZE; bp < bep; bp++) {
            if (*bp == ISEDGE) {
                jump++;
            }
        }
        
        if(jump >= s_WIN_SIZE/3) {  // TODO: should be directly return true
            potential = true;
        }
    }

#ifdef _DEBUG
    if(potential == true) {
        //DebugToolkit::normalizeAndShowHist(greyData, len);
        //DebugToolkit::normalizeAndShowHist(buffer, len);
    }
#endif

    return potential;
}

void Detector::fetchSubtitle(const uchar *greyData, int width, int height, vector<int> &start2end) {    
    const int theta = 8; // TODO
    const int theshold = 2;

    uchar *buffer1 = new uchar[height];
    int start, end, count;
    
    for (int i = 0 ; i < width; i++) {
        if(countJump(greyData, width, height, i, buffer1) >= theshold) {
            start = i;

            count = theta;
            for(i++ ; i < width; i++) {
                if(countJump(greyData, width, height, i, buffer1) < theshold) {
                    count--;
                } else {
                    count = theta;
                }

                if(count == 0) {
                    break;
                }
            }

            end = i - (theta - count);

            if(end - start > s_WIN_SIZE) {
                uchar *buffer2 = new uchar[end - start];
                
                if(isPotential(greyData + height/2*width + start, end - start, buffer2)) {
                    start2end.push_back((start > 2)? start - 2: 0);
                    start2end.push_back((end < width - 2)? end + 2: width);
                }
                
                delete[] buffer2;
            }
        }
    }

    delete[] buffer1;
}

int Detector::countJump(const uchar* greyData, int width, int height, int offset, uchar *buffer) {
    buffer[0] = buffer[height-1] = NOTEDGE;
    uchar *bp = buffer + 1;   // buffer pointer
    const uchar *bep = buffer + height - 1; // end pointer in iteration
    const uchar *gp = greyData + width + offset;    // greyData pointer

    for ( ; bp < bep; bp++, gp += width) {
        *bp = (abs(*(gp + width)*1.0f - *(gp - width)) > s_EDGE_THETA)? ISEDGE: NOTEDGE;
    }

    int jump = 0;
    for (bp = buffer, bep = buffer + height; bp < bep; bp++) {
        if (*bp == ISEDGE) {
            jump++;
        }
    }

    /*
    uchar *haha = new uchar[height];
    for(int i = 0; i < height; i++) {
        haha[i] = *(greyData + i*width + offset);
    }
    DebugToolkit::normalizeAndShowHist(haha, height, 3);*/

    return jump;
}
